package com.cskaoyan.javase.oop2._2polymorphic._2advantage;

/**
 * 多态的优缺点:
 * 案例:
 * 科学家要收集动物的叫声去研究，
 * 先要收集猫，狗两种动物的叫声研究。
 * 随后又扩展到其它动物，最后又需要研究人类。
 *
 * 我们使用多态,将方法的形参写父类类型,传入不同的子类对象,体现不同的行为,这就是多态的优点:
 *      1.要实现多态必须要继承，而继承提高了代码复用率，提升了程序的可维护性。（继承保证）
 *      2. 有了多态后，用父类引用指向不同子类对象，只需要调用同名方法，就能自动根据不同子类对象，得出不同的行为。
 *              这大幅度简化了代码，提升了程序的扩展性。（多态保证）
 *
 *
 * 最后需要研究人类的叫声,但是人比较特殊,让人来叫,不能抓过来,要请过来,还得加钱,给钱才叫
 * 所以人类具有独特的行为: 加钱
 *
 * 多态显而易见的缺点:
 *      把子类当成父类使用,子类对象会被父类引用限制功能,无法使用子类独有的功能.
 *
 * 但是这个缺点是可以弥补的,可以把父类的引用转换成子类的引用,这样就可以访问子类独有的行为了.
 *
 * 这种把父类引用转换成子类引用的类型转换是属于强制类型转换,叫"向下转换",需要程序员写代码手动完成
 * 语法:
 *   要强转引用的类名 对象名 = (要强转引用的类名)引用;
 *
 *   引用数据类型的强制类型转换(向下转型),是非常危险的.要想成功,条件十分苛刻,稍有不慎,就会强转失败,抛出异常报错: ClassCastException(类型转换异常)
 *   当强转失败时,就会抛出该异常.
 *
 * 接下来重点研究向下转型,成功的条件:
 *      1.能够通过编译
 *          向下转型是 父 --> 子
 *          所以必须是父类引用强转成子类引用,否者都会编译报错
 *          但是通过编译只是第一步,能不能成功,还要看代码运行
 *
 *      2.代码能够正常运行,成功强转
 *          能够成功强转的条件是:
 *          父类引用指向的子类对象
 *          就是要强转后的引用的对象或者子类对象
 *          比如Animal引用 ---> Person对象
 *          只能强转成
 *          Person引用 ---> Person对象
 *          除此之外,强转成Cat,Dog等Person的"兄弟姐妹"引用,都是错误,都会导致强转失败,抛出异常报错: ClassCastException(类型转换异常)
 *
 *
 * 强转如此危险,但也比较常用,所以Java中提供了专门的语法来检测能否强转成功.
 * 关键字: instanceof
 * 使用语法:
 *     引用名 instanceof 类名
 * 上述语法是一个表达式,会返回一个布尔类型的结果
 * 可以放在if中使用,做判断
 * 当instanceof前面的引用名所指向的对象是后面类名的对象或者子类对象时,返回true
 * 相反的,如果不是,就返回false
 * 当该语法返回true结果时,表示可以成功强转,不会抛出异常报错.
 *
 * 细节问题:
 * 如果引用指向null. null instanceof 类名 结果返回false 这么设计是为了避免空指针异常.
 *
 * @since 10:56
 * @author wuguidong@cskaoyan.onaliyun.com
 */
public class Demo {
    public static void main(String[] args) {
        collectAnimalVoice(new Cat());
        collectAnimalVoice(new Dog());
        collectAnimalVoice(new Pig());

        collectAnimalVoice(new Person());
    }

    /*// 收集研究猫的叫声
    public static void collectCatVoice(Cat c){
        // 抓一只猫来,让它叫
        c.shout();
        // 收集研究叫声
    }
    // 收集研究狗的叫声
    public static void collectCatVoice(Cat c){
        // 抓一只猫来,让它叫
        c.shout();
        // 收集研究叫声
    }*/

    /*
        子类可以当成父类使用
        所以如果父类做方法的形参
        具体的实参可以传入所有的它的子类对象

        所以写一个方法,形参写Animal,就可以传入所有的Animal子类对象
     */
    // 收集研究动物的叫声
    public static void collectAnimalVoice(Animal a) {
        /*
            收集人类叫声之前需要加钱,需要调用方法
            但是此时的引用是父类引用,访问范围被引用限制了
            不能访问子类独有的行为
            这里需要把Animal引用强转成Person引用

            强转十分危险,一定要在强转之前用instanceof做判断

         */
        if (a instanceof Person){
            // if执行,说明a指向的对象是Person的对象或者子类对象,总之可以正常强转成Person引用
            Person p = (Person) a;
            p.addMoney();
        }

        // 抓一只动物来,让它叫
        a.shout();
        // 收集研究叫声
    }
}

class Animal {
    public void shout() {
        System.out.println("叫!");
    }
}

class Cat extends Animal {
    @Override
    public void shout() {
        System.out.println("喵喵喵~");
    }
}

class Dog extends Animal {
    @Override
    public void shout() {
        System.out.println("汪汪汪~");
    }
}

class Pig extends Animal {
    @Override
    public void shout() {
        System.out.println("我是小猪佩奇!");
    }
}

class Person extends Animal {
    @Override
    public void shout() {
        System.out.println("嘤嘤嘤~");
    }

    public void addMoney(){
        System.out.println("这种事情,人家好害羞,让我做不行,得加钱!");
    }
}